package com.darkduck.fcap;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.prefs.Preferences;

import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.YuvImage;
import android.media.FaceDetector;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.util.Log;

final class DecodeHandler extends Handler {

  private static final String TAG = DecodeHandler.class.getSimpleName();
  
  private static final int DTECT_FACE_NUM = 10;

  private final CaptureActivity activity;
  private boolean running = true;
  private SharedPreferences sharedPreferences;

  DecodeHandler(CaptureActivity activity) {
    this.activity = activity;
    this.sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity);
  }

  @Override
  public void handleMessage(Message message) {
    if (!running) {
      return;
    }
    switch (message.what) {
      case R.id.decode:
        Log.i(TAG, "starting decode");
        decode((byte[]) message.obj, message.arg1, message.arg2);
        break;
      case R.id.quit:
        running = false;
        Looper.myLooper().quit();
        break;
    }
  }

    private void decode(byte[] data, int width, int height) {        
        YuvImage yuvimage = new YuvImage(data, ImageFormat.NV21, width, height, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvimage.compressToJpeg(new Rect(0, 0, width, height), 80, baos);

        Bitmap bitmap = BitmapFactory.decodeByteArray(baos.toByteArray(), 0,
                baos.toByteArray().length);
       
        //find faces in bitmap, return face bitmap array
        ArrayList<Bitmap> bitmapArray = detectBitmap(bitmap);
        if (bitmapArray != null) {            
            // send msg
            Message message = Message.obtain(activity.getHandler(), R.id.decode_succeeded, bitmapArray.size());
            Bundle bundle = new Bundle();
            bundle.putParcelableArrayList(DecodeThread.BITMAP_ARRAY, bitmapArray);
            message.setData(bundle);
            message.sendToTarget();
        }
        else {
            Message message = Message.obtain(activity.getHandler(), R.id.decode_failed);
            message.sendToTarget();
        }
        if (!bitmap.isRecycled())
            bitmap.recycle();
    }  

    private ArrayList<Bitmap> detectBitmap(Bitmap bitmap) {
        if (bitmap == null)
            return null;
        ArrayList<Bitmap> bitmapArray = null;
        FaceDetector.Face[] Faces = new FaceDetector.Face[DTECT_FACE_NUM];        
        FaceDetector detector = new FaceDetector(bitmap.getWidth(), bitmap.getHeight(),
                Faces.length);
        
        int faceNum = detector.findFaces(bitmap, Faces);        
        if (faceNum <= 0) 
            return null;
        
        bitmapArray = getBitmapArray(bitmap, Faces, faceNum);
        return bitmapArray;
    }
    
    ArrayList<Bitmap> getBitmapArray(Bitmap bitmap, FaceDetector.Face[] faces, int num) {
        ArrayList<Bitmap> bitmapArray = new ArrayList<Bitmap>();
        Bitmap faceBitmap = null;
        for (int i = 0; i < num; i++) {
            //check confidence of face
            Log.i(TAG, "face confidence:"+faces[i].confidence());
            String confidence = sharedPreferences.getString(PreferencesActivity.KEY_ACCURACY, PreferencesActivity.CONFIDENCE_LOW);           
            if (faces[i].confidence() < Float.parseFloat(confidence)) {
                Log.e(TAG, "face confidence:"+faces[i].confidence()+" is lower than preference value");
                //the confidence is lower
                return null;
            }
            PointF point = new PointF();
            faces[i].getMidPoint(point);            
            float di = faces[i].eyesDistance();            

            int x = (int)(point.x - di / 2 * 2.6);
            int y = (int)(point.y - di * 1.5);
            int w = (int)(di * 2.6);
            int h = (int)(di * 3.4);
            
            if (x < 0) 
                x = 0;
            if (y < 0)
                y = 0;
            if (x + w > bitmap.getWidth()) 
                w = bitmap.getWidth() - x;
            if (y + h > bitmap.getHeight())
                h = bitmap.getHeight() - y;
            
            faceBitmap = Bitmap.createBitmap(bitmap, x, y, w, h);
            
            //if BorderCheckBox checked, draw rectangle border
            if (PreferencesActivity.isBorderEnable(sharedPreferences)) {
                //draw bitmap rectangle frame
                Bitmap bitmapWithBorder = drawBorder(faceBitmap);
                
                if (bitmapWithBorder != null) 
                    bitmapArray.add(bitmapWithBorder);
                
                if (faceBitmap != null)
                    faceBitmap.recycle();
            } 
            else {
                if (faceBitmap != null) 
                    bitmapArray.add(faceBitmap);
            }
           
        }
        return bitmapArray;
        
    }
    
    private Bitmap drawBorder(Bitmap bitmap) {
        int width = PreferencesActivity.getBorderWidth(sharedPreferences);
        int color = PreferencesActivity.getBorderColor(sharedPreferences);
        Bitmap output = bitmap.copy(Bitmap.Config.ARGB_8888, true);
        Canvas canvas = new Canvas(output);

        final Paint paint = new Paint();
        final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
        paint.setStyle(Style.STROKE);
        paint.setAntiAlias(true);
        
        paint.setColor(color);
        paint.setStrokeWidth(width);
        canvas.drawRect(rect, paint);
        
        return output;
        
    }

}
